ICTSC2018 本戦 問題解説: LDAPが動かない

皆さんはLDAPを使ったことがありますか?
LDAPとは Lightweight Directory Access Protocol の略で、ディレクトリサービスのためのプロトコルです。ディレクトリサービスというのはその名前の通りディレクトリのようなツリー状の情報を管理するサービスで、その特徴からリソースの場所・設定などの情報を一元管理するのに用いられています。
とりわけ大きな組織においてはこのような情報を管理することにおいて大きなメリットがあります。管理すべき機器やユーザの数が膨大になってきたとき、必要に応じて素早く情報にアクセスできることが要求されるためです。
多くの企業ではディレクトリサービスを用いて情報を管理しており、ディレクトリサービスを提供するためのソフトウェアとしてはActive Directoryが代表的です。優れたGUIと長年Microsoftがメンテナンスしてきたことによる実績があり、何よりWindowsマシンを管理するのに長けているからです。
一方で、昔ながらのOpenLDAPを用いる場合もあります。OpenLDAPはLDAPのオープンソース実装の一つであり、高機能なActive Directoryなどに比べて軽量・高速に動作します。先に挙げたActive DirectoryもLDAPをベースに実装されており、LDAPへの理解を深めることはディレクトリサービスを知る上で欠かせないと言えます。
今回出題したトラブルは、LDAPに関する基本的な操作・知識を問う問題でした。LDAPを運用した経験がないと時間内に解くのはなかなか難しかったのではないかと思います。

問題文

最近LDAPを導入して、GitLabの認証とサーバへのSSHをLDAP経由で行えるように設定したのだがどうもうまく動いていないようだ。 原因を突き止めて、GitLabにログインできるようにしてほしい。必要に応じてサーバの設定を変えても構わないが、なるべくセキュリティレベルが下がらないようにしてほしい。また、admin (cn=admin,dc=finals,dc=ictsc) とoperator (cn=operator,ou=users,dc=finals,dc=ictsc) のパスワードが脆弱なので、可能であれば指定したものに変更してほしい。
なお、すべてのサーバはLDAPに登録してあるSSH鍵で入れるようになっているはずだが、こちらも同様に動いてないのでLDAPサーバのみにログインできるアカウントとLDAPに登録したSSH秘密鍵を用意した。必要に応じて使ってほしい。

問題のゴール状態

GitLabにLDAPユーザのoperator (パスワード変更済み) でログインに成功する

解説

今回の問題に登場するサーバは全部で2つです。

  • LDAPサーバ (dc.finals.ictsc)
  • GitLabサーバ (gitlab.finals.ictsc)

それぞれにSSSDがインストールされており、sss_ssh_authorizedkeys コマンドを用いてLDAPに登録されている公開鍵でログインできるようになっているはずが、SSHログインできなくなっている状態がスタートです。
また、GitLabのLDAPログインも同様に動かない (SSL errorが発生する) ので、これらを直してほしいという内容です。

初期状態をまとめると以下の図のようになります。

図で×印がついている部分がLDAPで認証を行う部分であり、これらをすべて復旧するのがゴールです。

問題のゴール自体はGitLabでLDAPログインできるようになることとなっていますが、実際には認証を直してGitLabサーバへのSSHログインができるようにならないとトラブルシューティングが行えないため、まずはSSHログインを復活させるのが1つ目のゴールになります。

SSL証明書の有効化

GitLabでLDAPログインを試みると以下のようなエラーメッセージが出ます。

Could not authenticate you from Ldapmain because “Ssl connect syscall returned=5 errno=0 state=sslv2/v3 read server hello a”.

これはそもそもSSL接続が確立できていないということなので、opensslで確認してみます。

~$ openssl s_client -connect 192.168.2.10:636
CONNECTED(00000003)
140350580467344:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 289 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol  : TLSv1.2
Cipher    : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg   : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1552100789
Timeout   : 300 (sec)
Verify return code: 0 (ok)
---

これを見ればわかるとおり、そもそも証明書が設定されていないことがわかります。
しかし /etc/ldap/slapd.conf を見ると TLSCACertificateFile=/etc/ldap/ssl/ca.crt などの設定がされているため本来は有効化されているはずですが、これらのパラメータは少なくともdebian系のslapdでは正しく動きません。よって適切なLDIFを書いて、手動で有効化してやる必要があります。

dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ldap/ssl/ca.crt
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/ssl/dc.finals.ictsc.key
-
add: olcTLSCertficateFile
olcTLSCertficateFile: /etc/ldap/ssl/dc.finals.ictsc.crt

SSL証明書のパーミッションも正しくしておきましょう。

~$ ls -lhat /etc/ldap/ssl/
total 24K
drwxr-xr-x 2 root root 4.0K Feb 17 02:07 .
-rw-r--r-- 1 root root 2.0K Feb 17 02:07 ca.crt
-rw-r--r-- 1 root root 5.9K Feb 17 02:07 dc.finals.ictsc.crt
-rw------- 1 root root 1.7K Feb 17 02:07 dc.finals.ictsc.key
drwxr-xr-x 7 root root 4.0K Feb 17 02:07 ..


...

~$ ls -lhat /etc/ldap/ssl/
total 24K
drwxr-xr-x 2 root root     4.0K Feb 17 02:07 .
-rw-r--r-- 1 root openldap 2.0K Feb 17 02:07 ca.crt
-rw-r--r-- 1 root openldap 5.9K Feb 17 02:07 dc.finals.ictsc.crt
-rw-r----- 1 root openldap 1.7K Feb 17 02:07 dc.finals.ictsc.key
drwxr-xr-x 7 root root     4.0K Feb 17 02:07 ..

先ほどのLDIFを olcSSL.ldif という名前で保存し、以下のコマンドで適用します。
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f olcSSL.ldif

SSSDで公開鍵が取得できない

サーバの/etc/ssh/sshd_configを見ると、AuthorizedKeysCommandディレクティブにsss_ssh_authorizedkeysが設定されていることがわかります。しかし試しにこれを実行してみても公開鍵が出力される様子がありません。

~$ /usr/bin/sss_ssh_authorizedkeys operator
~$

これが原因でSSHログインができないことがわかりますが、なぜ公開鍵が取得できないのでしょうか。LDAPの設定を見直しても公開鍵はちゃんと登録されています。

sss_ssh_authorizedkeysコマンドには実はデバッグオプションが存在します(これはhelpでも出てきません)。--debug 10という引数をつけてコマンドを呼び出すと、詳しいエラーメッセージを見ることができます。

~$ ~$ sss_ssh_authorizedkeys operator --debug 10
(Wed Mar 27 20:25:46:414921 2019) [sss_ssh_authorizedkeys] [main] (0x0040): sss_ssh_format_pubkey() failed (22): Invalid argument

このメッセージから、登録されている公開鍵のフォーマットが正しくないと予想できます。もう一度LDAPに登録されている鍵をみてみましょう。

~$ ldapvi -D 'cn=admin,dc=finals,dc=ictsc' -b 'dc=finals,dc=ictsc'
...
loginShell: /bin/bash
sshPublicKey:; ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJodlzzbHLCCfldHfG7xKlA4tl6t118hAdjbzuZIYCJELLFTwctlFVOBgZHs4JkT5Cgm7eK1VXL99w7SapNzhMs= operator@dc^M\

よく注意して見ると、公開鍵の末尾に改行が含まれていることがわかります。試しに改行を消してみるとどうなるでしょうか。

~$ sss_ssh_authorizedkeys operator
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJodlzzbHLCCfldHfG7xKlA4tl6t118hAdjbzuZIYCJELLFTwctlFVOBgZHs4JkT5Cgm7eK1VXL99w7SapNzhMs= operator@dc

ちゃんと取得することができました。秘密鍵でSSHログインが可能なことも確認できます。

~$ ssh -i files/id_ecdsa [email protected]
Last login: Sun Feb 17 03:38:42 2019 from 192.168.2.1
Could not chdir to home directory /home/operator: No such file or directory
operator@dc:/$

これで登録してある公開鍵でログインができるようになりました。

パスワードの変更

GitLabのトラブルシューティングに移る前にパスワードの変更をしておきます。

ldapviを用いてパスワードを変更しようとするとエラーが発生し変更できなかったり、phpLDAPadminにおいてはエラーすら出なかったりします。(この場合も依然として変更はできていません)

このような場合はアクセスコントロールを疑います。/etc/ldap/ldif/olcAccess.ldifが置いてあるのでこの中身をみてみましょう。

dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
# password should not be readable and only user can update
olcAccess: to attrs=userPassword
by self =sw
# uid, uidNumber, gidNumber should not be updated
olcAccess: to attrs=uid,uidNumber,gidNumber
by self read
by users read
# for ssh login
olcAccess: to attrs=sshPublicKey
by self write
by group.exact="cn=servers,ou=sgroups,dc=finals,dc=ictsc" read
# for sudo
olcAccess: to dn.children="ou=sudoers,dc=finals,dc=ictsc"
by group.exact="cn=servers,ou=sgroups,dc=finals,dc=ictsc" read
# not matched
olcAccess: to *
by self read
by users read
by anonymous auth
by * none

注目すべきはuserPasswordに対して設定してある項目です。by self =swはsearchとwriteをユーザー自身に許可する設定なので大丈夫なはずですが、by anonymous authがついていないのでbindができないようになっています。

よってolcAccess.ldifuserPasswordby anonymous authを追加してあげるとパスワードの変更が可能になります。

GitLabの証明書

SSSDのエラーを修正したことでGitLabにログインが可能になりますが、GitLabのログインページは依然としてエラーを出します。エラーメッセージを読むと証明書周りのエラーということがわかるので、/etc/gitlab/gitlab.rbの中身を確認します。

LDAPの設定部分にverify_certificates: trueとあるので、証明書がチェックに引っかかっていることが原因のようです。これを回避するためには証明書のチェックを無効にするのが一つの手ですが、セキュリティの観点からはあまり好ましい対策とは言えません。証明書 (dc.finals.ictsc) をシステムのチェーンに登録して有効な証明書とするのがベターです。

~$ sudo -s
~# cd /usr/share/ca-certificates/
~# openssl s_client -showcerts -connect dc.finals.ictsc:636 2>/dev/null | openssl x509 > rootCA.crt
~# echo "rootCA.crt" >> /etc/ca-certificates.conf
~# update-ca-certificates

GitLab上のユーザブロック解除

証明書のエラーを解決するとようやくLDAP認証が動くようになります。しかしoperatorユーザはなぜかログインできず、ブロックされている旨のメッセージが出ます。

これはすでにローカルでoperatorという名前のユーザが登録されていたことが原因ですが、解除するためにはadminアカウントが必要になります。operatorユーザでSSHログインするとsudoでrootになることができるので、GitLabのrails consoleに直接接続してパスワードを書き換えることができます。

~# gitlab-rails console production
user = User.where(id: 1).first
user.password = 'strongpassword'
user.password_confimation = 'strongpassword'
user.save!

これでadminとしてログインして、admin areaにあるユーザの設定でLDAPのoperatorユーザを有効にするとログインできるようになります。

採点基準

GitLabサーバにログインできるようになるのが一つ目のゴールなので、ここまで到達した場合は満点の50%が得られます。

  • LDAPユーザのパスワードを変更する (20%)
  • GitLabサーバにログインできるようになる (30%)
  • GitLabでLDAP認証を使えるようにする
    • LDAPをTLS通信に対応させる (30%)
    • operatorユーザのブロックを解除する (20%)

講評

今回の問題の一番のポイントはLDAPそのもではなく、Undocumentedな仕様を適切に探し出すことができるかという点にありました。OpenLDAPのslapd.confが意図した通りに動かない、SSSDが解釈できる公開鍵のフォーマットに隠された仕様があるなど、ソフトウェアは思わぬところで予想に反する挙動を示すことがあります。事前に知識を持っている場合はすぐに気づくことができますが、多くの場合はそうではありません。今回はLDAP+SSSD+GitLabという、意外と一般的だが普段は使ってなさそうな技術を意図的に選びました。

もっとも高得点を獲得したのはLDAPのSSL証明書の設定を解決したチームでした。残念ながらGitLabサーバへSSHできるところまでたどり着くチームはいませんでしたが、コンテスト時間がこれだけ短く、問題数が多いことを考えると仕方ないのかなと思います。また、得点の高さをみて手をつけなかったチームも結構いたのではないでしょうか。トラコンでは必ずしも問題を解ききる必要はないので、部分点解法を大量に稼いでいくのも一つの戦略です。